import * as step from '@flow-step/step-toolkit'
import * as fs from 'fs'
import FormData from 'form-data'
import axios from 'axios'
import {UploadInputs} from './upload-inputs'
import {getMetaData} from './meta-data'
import https from "https"
import {chunkUpload, MULTIPART_UPLOAD_THRESHOLD} from './package-chunk-upload'

/**
 * 格式化文件大小，自动选择合适的单位
 */
function formatFileSize(bytes: number): string {
    if (bytes === 0) return '0 B'
    
    const k = 1024
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
    const i = Math.floor(Math.log(bytes) / Math.log(k))
    
    if (i >= sizes.length) {
        return (bytes / Math.pow(k, sizes.length - 1)).toFixed(2) + ' ' + sizes[sizes.length - 1]
    }
    
    return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]
}

export async function artifactUpload(artifact: UploadInputs) {
    if (artifact.credential === undefined) {
        step.error(
            'Failed to get packages user credential, please check if the service connection is invalid!'
        )
        throw new Error('packages user credential is empty')
    }
    
    // 检查文件大小，决定是否使用分块上传
    const fileSize = fs.statSync(artifact.artifactFullName).size
    const fileSizeFormatted = formatFileSize(fileSize)
    step.info(`File size: ${fileSizeFormatted} (${fileSize} bytes)`)
    step.info(`File MD5: ${artifact.fileMD5}`)
    
    if (fileSize > MULTIPART_UPLOAD_THRESHOLD) {
        // 使用分块上传
        step.info('File size greater than 800MB, using chunk upload')
        await chunkUpload(artifact)
        return
    }
    
    // 使用普通上传
    step.info(
        `Upload ${artifact.artifactFullName} to PACKAGES, and artifact version is ${artifact.artifactVersion}`
    )

    let data = new FormData()
    data.append('file', fs.createReadStream(artifact.artifactFullName))
    data.append('fileName', artifact.artifact)
    data.append('downloadFileName', artifact.artifactName)
    data.append('version', artifact.artifactVersion)
    //「unable to verify the first certificate」忽略证书验证
    const agent = new https.Agent({
        rejectUnauthorized: false
    })
    let config = {
        httpsAgent: agent,
        method: 'post',
        url: `${artifact.packageHost}/api/protocol/${artifact.orgId}/generic/${artifact.repoId}/files`,
        headers: {
            Authorization: artifact.credential
        },
        data: data
    }
    let resultMd5 = ''
    await axios.request(config)
        .then(function (response: {data: any}) {
            //输出写入params
            let packagePath = `repoId=${artifact.repoId}&artifactName=${artifact.artifact}&artifactVersion=${artifact.artifactVersion}`
            let artifactSource = 'system-packages'
            let res = JSON.stringify(response.data)
            let jsonData = JSON.parse(res)
            resultMd5 = jsonData.object.fileMd5
            if (resultMd5 != artifact.fileMD5) {
                step.error(
                    `Md5 not matched, source fileMd5:${artifact.fileMD5}, upload fileMd5:${resultMd5}`
                )
                throw new Error('Failed to upload artifact')
            }
            let output = `${artifact.stepIdentifier}.ARTIFACT={"ARTIFACT_URL":"${jsonData.object.url}","ARTIFACT_NAME":"${artifact.artifactName}","ARTIFACT_MD5":"${jsonData.object.fileMd5}","WEB_DOWNLOAD_URL":"${packagePath}","artifact":"${artifact.artifactName}","type":"packages","downloadUrl":"${jsonData.object.url}","md5":"${jsonData.object.fileMd5}","repoId":"${artifact.repoId}","version":"${artifact.artifactVersion}"}`
            let packageInfo = `PACKAGE_INFO={"packageLabel":"${artifact.artifact}","packagePath":"${artifact.packageHost}/generic/${artifact.repoId}/artifacts?artifactName=${artifact.artifact}&version=${artifact.artifactVersion}","uploadType":"${artifact.uploadType}","repoId":"${artifact.repoId}"}`
            let artifacts = `{"source":"${artifactSource}","type":"package","packageLabel":"${artifact.artifact}","objectKey": "${artifact.fileId}","bucketName": "${artifact.bucketName}","webDownloadURL":"${packagePath}","objectKeyPrefix":"${artifact.fileParentId}","name":"${artifact.artifact}","version":"${artifact.artifactVersion}","repoId":"${artifact.repoId}","fileSize":${jsonData.object.fileSize}}`
            let artifactsList = new Array()
            artifactsList.push(JSON.parse(artifacts))
            step.addOutput(output);
            step.addOutput(packageInfo);
            step.addOutput(`ARTIFACTS=` + JSON.stringify(artifactsList));
            step.info(`Artifact download URL is: ${jsonData.object.url}`)
            step.info('Upload finished')
            fs.unlinkSync(artifact.artifactFullName)
        })
        .catch(function (error: any) {
            step.error(JSON.stringify(error.message))
            if(error.response && error.response.data){
                step.error(JSON.stringify(error.response.data))
            }
            fs.unlinkSync(artifact.artifactFullName)
            throw new Error('Failed to upload artifact')
        })
}

export async function uploadMetaData(artifact: UploadInputs) {
    const metaDataObj = getMetaData()
    let metaData = JSON.stringify(metaDataObj)
    step.info(metaData)
    const agent = new https.Agent({
        rejectUnauthorized: false
    })
    const orgId = process.env['ENGINE_GLOBAL_PARAM_ORGANIZATION_ID']
    var formData = new URLSearchParams() 
    formData.append('artifact',artifact.artifact)
    formData.append('artifactVersion',artifact.artifactVersion)
    formData.append('fileMd5',artifact.fileMD5)
    formData.append('nodeName','BUILD')
    formData.append('body',metaData)

    const commonConfig = {
        httpsAgent: agent,
        method: 'post',
        headers: {
            Authorization: artifact.credential,
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        data: formData
    }
    const oldArchitectureURL = `${artifact.packageHost}/api/external/v2/${artifact.regionId}/GENERIC/${artifact.repoId}/metadata/add?orgId=${orgId}`
    const newArchitectureURL = `${artifact.packageHost}/api/external/generic/${artifact.repoId}/metadata?orgId=${orgId}`
    //默认使用老架构配置
    let config = {
        ...commonConfig,
        url: oldArchitectureURL
    }
    if (artifact.metaUpload != 'NO') {
        step.info(`Upload meta data`)
        try {
            const response = await axios.request(config)
            step.info(`MetaData upload result is: ${response.status}, ${JSON.stringify(response.data)}`)
        } catch(error) {
            if(axios.isAxiosError(error) && error.response && error.response.status === 404) {
                // 如果老架构接口返回404，则尝试新架构接口
                config.url = newArchitectureURL
                try {
                    const newArchRespond = await axios(config)
                    step.info(`MetaData upload result is: ${newArchRespond.status}, ${JSON.stringify(newArchRespond.data)}`)
                } catch(newError) {
                    step.error(`Upload metaData occurs error !!!`);
                    if (axios.isAxiosError(newError) && newError.response) {
                        step.info(JSON.stringify(newError.response.data));
                    } else {
                        step.error(`New architecture request error: ${newError}`);
                    }
                }
            } else {
                step.error(`request error: ${error}`);
            }
        }
    }

}
